﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;

namespace Oliver.Controls
{
    public class ImageRotator : Panel
    {
        public ImageRotator() : base()
        {
            if (Preview)
            {
                _timer = new DispatcherTimer();
                _timer.Interval = new TimeSpan(0, 0, WaitingSeconds);
                _timer.Tick += _timer_Tick;
            }
        }

        private bool _preview = true;
        public bool Preview
        {
            get { return _preview; }
            set { _preview = value; }
        }

        void _timer_Tick(object sender, EventArgs e)
        {
            _timer.Stop();
            FadeOutImage();
        }

        private void ShowAnimation(int offset)
        {
            _timer.Start();
            if (this.Resources.Contains("showanimation"))
            {
                this.Resources.Remove("showanimation");
            }

            Storyboard showanimation = new Storyboard();
            this.Resources.Add("showanimation", showanimation);

            int ms = (int)this.GetValue(WaitingSecondsProperty) * 1000 
                + (int)this.GetValue(MillisecondsFaidingProperty);
            TimeSpan ts = new TimeSpan(0, 0, 0, 0, ms);
            Duration duration = new Duration(ts);

            int idx = this.Children.Count - offset;
            Image image = this.Children[idx] as Image;

            double scaleFactor = (double)GetValue(ScaleFactorProperty);
        
            // Scale X
            DoubleAnimation daScaleTransformX = new DoubleAnimation();
            daScaleTransformX.Duration = duration;
            daScaleTransformX.To = scaleFactor;
            showanimation.Children.Add(daScaleTransformX);
            Storyboard.SetTarget(daScaleTransformX, image);
            Storyboard.SetTargetProperty(daScaleTransformX
                , new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleX)"));

            // Scale Y
            DoubleAnimation daScaleTransformY = new DoubleAnimation();
            daScaleTransformY.Duration = duration;
            daScaleTransformY.To = scaleFactor;
            showanimation.Children.Add(daScaleTransformY);
            Storyboard.SetTarget(daScaleTransformY, image);
            Storyboard.SetTargetProperty(daScaleTransformY
                , new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleY)"));

            // Rendertransform
            Random rnd = new Random();
            double centerX = rnd.NextDouble();
            double centerY = rnd.NextDouble();
            //image.RenderTransformOrigin = new Point(centerX, centerY);

            PointAnimation daRenderTransformOrigin = new PointAnimation();
            daRenderTransformOrigin.Duration = duration;
            daRenderTransformOrigin.To = new Point(centerX, centerY);
            showanimation.Children.Add(daRenderTransformOrigin);
            Storyboard.SetTarget(daRenderTransformOrigin, image);
            Storyboard.SetTargetProperty(daRenderTransformOrigin
                , new PropertyPath("(UIElement.RenderTransformOrigin)"));

            showanimation.Completed += showanimation_Completed;
            showanimation.Begin();
        }

        void showanimation_Completed(object sender, EventArgs e)
        {
            Storyboard sb = sender as Storyboard;
            sb.Completed -= showanimation_Completed;
        }

        private void CreateTransformGroup(UIElement element)
        {
            TransformGroup tg = null;

            if (element.RenderTransform == null
                || !(element.RenderTransform is TransformGroup))
            {
                tg = new TransformGroup();
                element.RenderTransform = tg;
            }
            else
            {
                tg = element.RenderTransform as TransformGroup;
            }

            // Translate Transform
            if (tg.Children.OfType<TranslateTransform>().Count() == 0)
            {
                TranslateTransform tt = new TranslateTransform();
                tg.Children.Add(tt);
            }

            // Scale Transform
            if (tg.Children.OfType<ScaleTransform>().Count() == 0)
            {
                ScaleTransform st = new ScaleTransform();
                tg.Children.Add(st);
            }
        }

        private void FadeOutImage()
        {
            if (this.Resources.Contains("fadeout"))
            {
                this.Resources.Remove("fadeout");
            }

            ShowAnimation(2);

            Storyboard fadeOut = new Storyboard();
            this.Resources.Add("fadeout", fadeOut);

            int ms = (int)this.GetValue(MillisecondsFaidingProperty);
            TimeSpan ts = new TimeSpan(0, 0, 0, 0, ms);
            Duration duration = new Duration();

            int idx = this.Children.Count - 1;
            Image image = this.Children[idx] as Image;

            // Opacity
            DoubleAnimation daOpacity = new DoubleAnimation();
            daOpacity.Duration = duration;
            daOpacity.To = 0;
            fadeOut.Children.Add(daOpacity);
            Storyboard.SetTarget(daOpacity, image);
            Storyboard.SetTargetProperty(daOpacity, new PropertyPath("(UIElement.Opacity)"));

            fadeOut.Completed += fadeOut_Completed;
            fadeOut.Begin();
        }

        void fadeOut_Completed(object sender, EventArgs e)
        {
            ChangeImage();
            Storyboard sb = sender as Storyboard;
            sb.Completed -= fadeOut_Completed;
            _timer.Start();
        }

        private void ChangeImage()
        {
            int idx = this.Children.Count - 1;
            Image image = this.Children[idx] as Image;
            this.Children.Remove(image);
            this.Children.Insert(0, image);
            image.Opacity = 1;

            // Reset Transformations
            TransformGroup tg = (image.RenderTransform as TransformGroup);
            
            foreach (Transform t in tg.Children)
            {
                if (t is TranslateTransform)
                {
                    TranslateTransform tt = t as TranslateTransform;
                    tt.X = 0;
                    tt.Y = 0;
                }
                else if (t is ScaleTransform)
                {
                    ScaleTransform st = t as ScaleTransform;
                    st.ScaleX = 1;
                    st.ScaleY = 1;
                }
            }
        }

        DispatcherTimer _timer;

        #region Arrange Override

        protected override Size ArrangeOverride(Size finalSize)
        {
            RectangleGeometry rect = new RectangleGeometry();
            rect.Rect = new Rect(0,0, finalSize.Width, finalSize.Height);
            this.Clip = rect;

            foreach (Image image in this.Children.OfType<Image>())
            {
                image.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
                CreateTransformGroup(image);
            }

            ShowAnimation(1);
            return base.ArrangeOverride(finalSize);
        }

        #endregion

        #region Dependancy Properties

        public int WaitingSeconds
        {
            get { return (int)GetValue(WaitingSecondsProperty); }
            set { SetValue(WaitingSecondsProperty, value); }
        }

        public static readonly DependencyProperty WaitingSecondsProperty =
                DependencyProperty.Register("WaitingSeconds", typeof(int),
                typeof(ImageRotator),
                new PropertyMetadata(3,
                new PropertyChangedCallback(OnPropertyChanged)));

        public double ScaleFactor
        {
            get { return (double)GetValue(ScaleFactorProperty); }
            set { SetValue(ScaleFactorProperty, value); }
        }

        public static readonly DependencyProperty ScaleFactorProperty =
                DependencyProperty.Register("ScaleFactor", typeof(double),
                typeof(ImageRotator),
                new PropertyMetadata(1.2,
                new PropertyChangedCallback(OnPropertyChanged)));

        public int MillisecondsFaiding
        {
            get { return (int)GetValue(MillisecondsFaidingProperty); }
            set { SetValue(MillisecondsFaidingProperty, value); }
        }

        public static readonly DependencyProperty MillisecondsFaidingProperty =
                DependencyProperty.Register("MillisecondsFaiding", typeof(int),
                typeof(ImageRotator),
                new PropertyMetadata(500,
                new PropertyChangedCallback(OnPropertyChanged)));

        protected static void OnPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            ImageRotator i = obj as ImageRotator;
            i.InvalidateArrange();
        }

        #endregion
    }
}
